2aff8b5c |
1 | <?xml version="1.0" encoding="utf-8" ?> |
2 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" |
3 | "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> |
4 | <html xmlns="http://www.w3.org/1999/xhtml"> |
5 | <head> |
6 | <title>Metaobject Protocols</title> |
7 | <meta name="generator" content="muse.el" /> |
8 | <meta http-equiv="Content-Type" |
9 | content="text/html; charset=utf-8" /> |
10 | <link rel="stylesheet" href="default.css" media="screen" /> |
11 | </head> |
12 | <body> |
13 | <h1>Metaobject Protocols</h1> |
14 | <div class="contents"> |
15 | <dl> |
16 | <dt> |
17 | <a href="#sec1">Background</a> |
18 | </dt> |
19 | <dd> |
20 | <dl> |
21 | <dt> |
22 | <a href="#sec2">Object Protocols</a> |
23 | </dt> |
24 | <dt> |
25 | <a href="#sec3">CLOS Way of OO</a> |
26 | </dt> |
27 | <dd> |
28 | <dl> |
29 | <dt> |
30 | <a href="#sec4">Classes for scratch data and types</a> |
31 | </dt> |
32 | <dt> |
33 | <a href="#sec5">Generics with methods that implement protocols</a> |
34 | </dt> |
35 | </dl> |
36 | </dd> |
37 | </dl> |
38 | </dd> |
39 | <dt> |
40 | <a href="#sec6">Limitations of Default Language Behavior</a> |
41 | </dt> |
42 | <dd> |
43 | <dl> |
44 | <dt> |
45 | <a href="#sec7">Slot Storage</a> |
46 | </dt> |
47 | <dt> |
48 | <a href="#sec8">Design Patterns</a> |
49 | </dt> |
50 | </dl> |
51 | </dd> |
52 | <dt> |
53 | <a href="#sec9">Metasoftware</a> |
54 | </dt> |
55 | <dd> |
56 | <dl> |
57 | <dt> |
58 | <a href="#sec10">Runtime Generated Classes</a> |
59 | </dt> |
60 | <dt> |
61 | <a href="#sec11">Object Inspection</a> |
62 | </dt> |
63 | </dl> |
64 | </dd> |
65 | <dt> |
66 | <a href="#sec12">Metaobject Protocols</a> |
67 | </dt> |
68 | <dd> |
69 | <dl> |
70 | <dt> |
71 | <a href="#sec13">Limited/Generalized Internals of the Implementation</a> |
72 | </dt> |
73 | <dt> |
74 | <a href="#sec14">Classes of MOPs</a> |
75 | </dt> |
76 | <dd> |
77 | <dl> |
78 | <dt> |
79 | <a href="#sec15">Reflective</a> |
80 | </dt> |
81 | <dt> |
82 | <a href="#sec16">Intercessory</a> |
83 | </dt> |
84 | </dl> |
85 | </dd> |
86 | <dt> |
87 | <a href="#sec17">Violation of Encapsulation?</a> |
88 | </dt> |
89 | </dl> |
90 | </dd> |
91 | <dt> |
92 | <a href="#sec18">MOP Design Principles</a> |
93 | </dt> |
94 | <dd> |
95 | <dl> |
96 | <dt> |
97 | <a href="#sec19">Layered Protocol</a> |
98 | </dt> |
99 | <dd> |
100 | <dl> |
101 | <dt> |
102 | <a href="#sec20">Top level <strong>must</strong> call lower level functions</a> |
103 | </dt> |
104 | <dt> |
105 | <a href="#sec21">Lower level methods are easier to customize</a> |
106 | </dt> |
107 | </dl> |
108 | </dd> |
109 | <dt> |
110 | <a href="#sec22">Functional Where Possible</a> |
111 | </dt> |
112 | <dd> |
113 | <dl> |
114 | <dt> |
115 | <a href="#sec23">Memoization</a> |
116 | </dt> |
117 | <dt> |
118 | <a href="#sec24">Cleaner Code</a> |
119 | </dt> |
120 | </dl> |
121 | </dd> |
122 | <dt> |
123 | <a href="#sec25">Procedural Only Where Neccesary</a> |
124 | </dt> |
125 | </dl> |
126 | </dd> |
127 | <dt> |
128 | <a href="#sec26">Examples</a> |
129 | </dt> |
130 | <dd> |
131 | <dl> |
132 | <dt> |
133 | <a href="#sec27">Object Inspector</a> |
134 | </dt> |
135 | <dt> |
136 | <a href="#sec28">Observer Design Pattern</a> |
137 | </dt> |
138 | <dt> |
139 | <a href="#sec29">Real World</a> |
140 | </dt> |
141 | <dd> |
142 | <dl> |
143 | <dt> |
144 | <a href="#sec30">UCW and Arnesi</a> |
145 | </dt> |
146 | <dt> |
147 | <a href="#sec31">CLSQL</a> |
148 | </dt> |
149 | <dt> |
150 | <a href="#sec32">Elephant</a> |
151 | </dt> |
152 | </dl> |
153 | </dd> |
154 | </dl> |
155 | </dd> |
156 | <dt> |
157 | <a href="#sec33">Sources &amp; Further Reading</a> |
158 | </dt> |
159 | <dd> |
160 | <dl> |
161 | <dt> |
162 | <a href="#sec34">Sources</a> |
163 | </dt> |
164 | <dd> |
165 | <dl> |
166 | <dt> |
167 | <a href="#sec35">The Art of the Metaobject Protocol</a> |
168 | </dt> |
169 | <dt> |
170 | <a href="#sec36">CLOS MOP Specification</a> |
171 | </dt> |
172 | <dt> |
173 | <a href="#sec37">Metaobject Protocols: Why We Want Them and What Else They Can Do</a> |
174 | </dt> |
175 | <dt> |
176 | <a href="#sec38">Why Are Black Boxes so Hard to Reuse?</a> |
177 | </dt> |
178 | </dl> |
179 | </dd> |
180 | <dt> |
181 | <a href="#sec39">Further Reading</a> |
182 | </dt> |
183 | <dd> |
184 | <dl> |
185 | <dt> |
186 | <a href="#sec40">A Metaobject Protocol for C++</a> |
187 | </dt> |
188 | <dt> |
189 | <a href="#sec41">Open Implementations and Metaobject Protocols</a> |
190 | </dt> |
191 | </dl> |
192 | </dd> |
193 | </dl> |
194 | </dd> |
195 | </dl> |
196 | </div> |
197 | |
198 | |
199 | <!-- Page published by Emacs Muse begins here --><p>In Fall of 2006 I did a small project on Metaobject Protocols for my |
200 | CS 331 class. Here lie my notes which may perhaps be useful to |
201 | others. I hope to expand them into something more useful over time.</p> |
202 | |
203 | <h2><a name="sec1" id="sec1"></a> |
204 | Background</h2> |
205 | |
206 | <h3><a name="sec2" id="sec2"></a> |
207 | Object Protocols</h3> |
208 | |
209 | <p class="first">An object protocol is a set of methods and specification of the |
210 | interactions between the methods which provide some generic behavior |
211 | (e.g. of a sequence) that are then implemented by classes which |
212 | conform to the protocol (e.g. a vector or list). In most object |
213 | systems a class contains both the methods which implement a protocol |
214 | and the data used by the implementation. The intent is to emulate |
215 | state machines which pass messages between each other.</p> |
216 | |
217 | |
218 | <h3><a name="sec3" id="sec3"></a> |
219 | CLOS Way of OO</h3> |
220 | |
221 | <p class="first">The Common Lisp Object System (CLOS) is different. It separates |
222 | the data and method concepts into classes and generics. A class |
223 | contains data fields only, and a generic has methods specialized for |
224 | certain types attached to it. This seems a bit weird at first, but is |
225 | significantly more powerful as it encourages complete encapsulation |
226 | through its use of classes primarily for method specialization rather |
227 | than for state storage.</p> |
228 | |
229 | |
230 | <h4><a name="sec4" id="sec4"></a> |
231 | Classes for scratch data and types</h4> |
232 | |
233 | <p class="first">In CLOS classes store data in slots (which are the same as data |
234 | members). Encapsulation is not provided; any bit of code can use |
235 | <code>slot-value</code> to access or set the value of a slot. This may seem odd at |
236 | first, but encapsulation is of questionable importance as the slots |
237 | are meant only to be used by the protocol defined around the class.</p> |
238 | |
239 | <p>Classes are defined with defclass</p> |
240 | |
241 | <pre class="src"> |
242 | (<span style="color: #00ffff;">defclass</span> <span style="color: #98fb98;">name</span> (superclasses ...) |
243 | ((slot-name <span style="color: #b0c4de;">:accessor</span> slot-accessor ...) |
244 | ...) |
245 | (class-options ...)) |
246 | |
247 | (<span style="color: #00ffff;">defclass</span> <span style="color: #98fb98;">example</span> () |
248 | ((foo <span style="color: #b0c4de;">:accessor</span> foo-of <span style="color: #b0c4de;">:initform</span> 5))) |
249 | |
250 | (<span style="color: #00ffff;">defclass</span> <span style="color: #98fb98;">example-child</span> (example) |
251 | ((bar <span style="color: #b0c4de;">:accessor</span> bar-of <span style="color: #b0c4de;">:initform</span> (list 1 2 3)))) |
252 | </pre> |
253 | |
254 | <p>Slot defintions have several option; the above example shows only the |
255 | <code>:accessor</code> and <code>:initform</code> options which are the most commonly |
256 | used. <code>:accessor</code> generates an accessor for the slot (e.g. if you have |
257 | an instance of <code>example</code> you can <code>(setf (foo-of some-example-instance) 'some-value)</code> to set and <code>(foo-of some-example-instance)</code> to access the |
258 | value). <code>:initform</code> provides a default initial value for the slot as a |
259 | symbolic expression to be evaluated when an instance is created.</p> |
260 | |
261 | |
262 | <h4><a name="sec5" id="sec5"></a> |
263 | Generics with methods that implement protocols</h4> |
264 | |
265 | <p class="first">Generics are like normal functions in Lisp, but they only provide a |
266 | lambda list (parameter list). Methods are added to the generic which |
267 | specialize on the types of their parameters, and provide the actual |
268 | implementation. This allows for rich layered protocols to be developed |
269 | which can enable selective modification of individual facets with |
270 | minimal code.</p> |
271 | |
272 | <pre class="src"> |
273 | (<span style="color: #00ffff;">defgeneric</span> <span style="color: #87cefa;">generic</span> (parameters ...) |
274 | (options) ...) |
275 | |
276 | (<span style="color: #00ffff;">defmethod</span> <span style="color: #87cefa;">generic-name</span> ((parameter type) parameter ...) |
277 | <span style="color: #b3b3b3;">"documentation string"</span> |
278 | body) |
279 | |
280 | (<span style="color: #00ffff;">defgeneric</span> <span style="color: #87cefa;">foo</span> (bar baz quux) |
281 | (<span style="color: #b0c4de;">:documentation</span> <span style="color: #b3b3b3;">"Process the baz with the quux capacitor to make the |
282 | foo widget fly into the sky at warp speed"</span>)) |
283 | |
284 | (<span style="color: #00ffff;">defmethod</span> <span style="color: #87cefa;">foo</span> ((bar example) baz (quux capacitor)) |
285 | (launch bar (process-with quux baz))) |
286 | </pre> |
287 | |
288 | <p>A method lambda list differs from a normal lambda list only in that it |
289 | can specify the type of the parameter using the notation <code>(name type)</code>. |
290 | Note also that methods can specialize on the types of every |
291 | argument and not just the first one. This is quite powerful for |
292 | reasons outside of the scope of this presentation.</p> |
293 | |
294 | |
295 | |
296 | |
297 | <h2><a name="sec6" id="sec6"></a> |
298 | Limitations of Default Language Behavior</h2> |
299 | |
300 | <p class="first">The behavior of a language is a compromise between many competing |
301 | issues that attempts to be as generally useful as possible, and most |
302 | applications will have no issue with the default behavior. There are, |
303 | however, certain applications that could be cleanly written with minor |
304 | modifications to the behavior of the language, but would be impossible |
305 | or quite difficult to write otherwise.</p> |
306 | |
307 | <h3><a name="sec7" id="sec7"></a> |
308 | Slot Storage</h3> |
309 | |
310 | <p class="first">Most languages choose to preallocate storage for all of the slots of |
311 | an instance. Imagine a contact database that stored information about |
312 | people as slots of the class. There may be dozens of slots, but often |
313 | many of them will be left blank. If slot storage is preallocated much |
314 | memory will be wasted and the system may not be able to fit into the |
315 | memory of the hardware it must run on (perhaps for financial reasons, |
316 | huge datasets, etc.).</p> |
317 | |
318 | <p>To save memory the author of the contact database must implement his |
319 | own system to store properties and allocate them lazily. This |
320 | represents a fair bit of effort, and would implement a system that |
321 | differed from the existing slot system of classes only in the method |
322 | of data storage.</p> |
323 | |
324 | <p>It would be useful if there were a way to customize instance |
325 | allocation. The customizations would be minor and require overriding |
326 | only the initial allocation behavior and the behavior of the first |
327 | assignment to the slot. It is a a trivial problem in a language that |
328 | allows customization of these.</p> |
329 | |
330 | |
331 | <h3><a name="sec8" id="sec8"></a> |
332 | Design Patterns</h3> |
333 | |
334 | <p class="first">Design Patterns are generalized versions of common patterns found in |
335 | programs. Many of them are merely methods to get around deficiencies |
336 | in the language, and can be quite messy to implement in some |
337 | languages.</p> |
338 | |
339 | |
340 | |
341 | <h2><a name="sec9" id="sec9"></a> |
342 | Metasoftware</h2> |
343 | |
344 | <p class="first">Some types of programs could be written easily if the language were |
345 | customizable, but are nearly impossible to write when it is not.</p> |
346 | |
347 | <h3><a name="sec10" id="sec10"></a> |
348 | Runtime Generated Classes</h3> |
349 | |
350 | <p class="first">Say you wanted to write a video game where players could create their |
351 | own objects, attach behaviors to the objects, and perhaps mix |
352 | different objects together to create new ones. When you abstract the |
353 | problem this looks just like an object system! Wouldn't it be nice if |
354 | your program could create new objects and methods on the fly portably?</p> |
355 | |
356 | |
357 | <h3><a name="sec11" id="sec11"></a> |
358 | Object Inspection</h3> |
359 | |
360 | <p class="first">Imagine if you were developing a complicated program with many |
361 | different objects that interacted in fairly complex ways. A tool to |
362 | inspect the structure of objects while debugging would be quite |
363 | useful, but in a traditional language would be impossible to implement |
364 | portably. This could force you to purchase a certain compiler |
365 | implementation which provided one, and even then would more than |
366 | likely not be customizable.</p> |
367 | |
368 | <p>This problem can be generalized to apply to most debugging tools; it |
369 | would be useful to write such tools portably because users of the |
370 | <em>language</em> and not the <em>compiler</em> need to debug software. Sharing |
371 | infrastructure would result in better tools (more developers), and |
372 | save man-years of wasted effort that comes with having to rewrite |
373 | non-portable functionality from scratch multiple times.</p> |
374 | |
375 | |
376 | |
377 | <h2><a name="sec12" id="sec12"></a> |
378 | Metaobject Protocols</h2> |
379 | |
380 | <h3><a name="sec13" id="sec13"></a> |
381 | Limited/Generalized Internals of the Implementation</h3> |
382 | |
383 | <p class="first">A Metaobject protocol is a generalized and limited subset of the |
384 | underlying implementation of the language. It is generalized and |
385 | limited in scope to allow for multiple implementation strategies; |
386 | this, along with careful design, is essential because programming |
387 | language research is ever advancing and new techniques for creating |
388 | more reliable and faster implementations are still being discovered.</p> |
389 | |
390 | <p>This subset of the implementation is exported as a set of methods on |
391 | metaobjects. Thus the system is implemented in itself. The system can |
392 | then be customized using the extension and overriding features of the |
393 | system.</p> |
394 | |
395 | |
396 | <h3><a name="sec14" id="sec14"></a> |
397 | Classes of MOPs</h3> |
398 | |
399 | <h4><a name="sec15" id="sec15"></a> |
400 | Reflective</h4> |
401 | |
402 | <p class="first">A reflective MOP provides a functional/procedural interface to |
403 | information about the system. It exposes class relationships, the |
404 | methods are attached to a generic, etc. A reflective MOP often |
405 | provides some functionality for creating new classes at runtime.</p> |
406 | |
407 | <h5>Example: Object Inspector</h5> |
408 | |
409 | |
410 | <h5>Example: Runtime Generated Classes and Methods</h5> |
411 | |
412 | |
413 | |
414 | <h4><a name="sec16" id="sec16"></a> |
415 | Intercessory</h4> |
416 | |
417 | <p class="first">Intercessory MOPs allow the user to customize language behavior by |
418 | implementing methods which override certain aspects of the language |
419 | behavior. This class of MOPs are what make MOPs especially |
420 | powerful. No longer must a problem be restructured to fit the |
421 | implementation language; the underyling language can be reshaped to |
422 | fit the task at hand, and obfuscation of the intended structure of the |
423 | application can be avoided.</p> |
424 | |
425 | <h5>Example: Lazily Allocated Slots</h5> |
426 | |
427 | |
428 | <h5>Example: Observer Design Pattern</h5> |
429 | |
430 | |
431 | |
432 | |
433 | <h3><a name="sec17" id="sec17"></a> |
434 | Violation of Encapsulation?</h3> |
435 | |
436 | <p class="first">A MOP may seem like a violation of encapsulation by revealing some |
437 | implementation details, but in reality a well designed protocol does |
438 | not reveal anything which was not already exposed. Implementation |
439 | decisions affect users, and some of these details do leak through to |
440 | higher levels (e.g. the memory layout of slots). Implicit in the |
441 | protocol specification are these implementation details, and the MOP |
442 | merely makes this limited subset available for customization.</p> |
443 | |
444 | <p>A MOP makes it possible to customize certain implementation decisions |
445 | that do not <strong>radically</strong> alter the behavior of the base language. The |
446 | conceptual vocabulary of the system retains its meaning, and so code |
447 | written in one dialect can interact with code written in another |
448 | without knowing that they speak different ones.</p> |
449 | |
450 | |
451 | |
452 | <h2><a name="sec18" id="sec18"></a> |
453 | MOP Design Principles</h2> |
454 | |
455 | <h3><a name="sec19" id="sec19"></a> |
456 | Layered Protocol</h3> |
457 | |
458 | <p class="first">A layered protocol design is good for both meta and normal object |
459 | protocols, and enables a combinatorial explosion of customizations to |
460 | the protocol.</p> |
461 | |
462 | <h4><a name="sec20" id="sec20"></a> |
463 | Top level <strong>must</strong> call lower level functions</h4> |
464 | |
465 | <p class="first">The top level methods of a layered metaobject protocol are required to |
466 | call certain methods to perform some tasks. This both makes it easier |
467 | to customize the top level methods (which perform very broad tasks) by |
468 | providing some pieces of them for the programmer, and allows more |
469 | customization to be done by opening up the replacement of lower level |
470 | functions as a way to alter a small detail of the high level behavior.</p> |
471 | |
472 | |
473 | <h4><a name="sec21" id="sec21"></a> |
474 | Lower level methods are easier to customize</h4> |
475 | |
476 | <p class="first">The lower level methods of a MOP are limited in scope and can be |
477 | implemented easily. Often the changes to language behavior that are |
478 | wanted are very small, and having methods that perform simple tasks |
479 | which are often customized reduces the effort required to extend the |
480 | system.</p> |
481 | |
482 | |
483 | |
484 | <h3><a name="sec22" id="sec22"></a> |
485 | Functional Where Possible</h3> |
486 | |
487 | <p class="first">Functional protocols are preferred for MOPs (and object protocol in |
488 | general). Functional protocols open up several optimizations for the |
489 | implementation without burdening the user of the protocol.</p> |
490 | |
491 | <h4><a name="sec23" id="sec23"></a> |
492 | Memoization</h4> |
493 | |
494 | <p class="first">Memoization is the process of saving the results of a function call |
495 | for future use. This avoids expensive recomputation of values which |
496 | have not changed (recall that a true function will always return the |
497 | same result when given the same arguments).</p> |
498 | |
499 | <p>A functional MOP can be optimized easily by exploiting this property |
500 | to memoize the return values of calls to expensive operations. A MOP |
501 | must be be very fast to avoid making programs unusably slow, and |
502 | memoization is able to give an appreciable speedup in many cases |
503 | without an insignificant burden on memory usage.</p> |
504 | |
505 | <h5>Constant Shared Return Values</h5> |
506 | |
507 | <p>Disallowing the modification of values returned by protocol methods |
508 | allows the implementation to return large data structures by reference |
509 | to avoid expensive copying without having to do expensive data |
510 | integrity checks.</p> |
511 | |
512 | |
513 | |
514 | <h4><a name="sec24" id="sec24"></a> |
515 | Cleaner Code</h4> |
516 | |
517 | |
518 | |
519 | <h3><a name="sec25" id="sec25"></a> |
520 | Procedural Only Where Neccesary</h3> |
521 | |
522 | <p class="first">Some operations like method invocation are inheretly stateful and so |
523 | must use a procedural protocol. There is no benefit to be gained from |
524 | using a functional protocol, and indeed an attempt would result in |
525 | obtuse code that severely restricted the implementor. Do note that |
526 | only a very small part of method invocation is stateful (the actual |
527 | call), and most of it can be implemented functionally (e.g. computing |
528 | the discriminating function).</p> |
529 | |
530 | |
531 | |
532 | <h2><a name="sec26" id="sec26"></a> |
533 | Examples</h2> |
534 | |
535 | <h3><a name="sec27" id="sec27"></a> |
536 | Object Inspector</h3> |
537 | |
538 | <p class="first">A primitive portable object inspector is presented here.</p> |
539 | |
540 | <pre class="src"> |
541 | (<span style="color: #00ffff;">defgeneric</span> <span style="color: #87cefa;">example-inspect</span> (instance) |
542 | (<span style="color: #b0c4de;">:documentation</span> <span style="color: #b3b3b3;">"Simple object inspector using CLOS MOP"</span>)) |
543 | |
544 | (<span style="color: #00ffff;">defmethod</span> <span style="color: #87cefa;">example-inspect</span> ((instance t)) |
545 | (format t <span style="color: #b3b3b3;">"Simple Object~% Value: ~S~%"</span> instance)) |
546 | |
547 | (<span style="color: #00ffff;">defmethod</span> <span style="color: #87cefa;">example-inspect</span> ((instance standard-object)) |
548 | (<span style="color: #00ffff;">let</span> ((class (class-of instance))) |
549 | (format t <span style="color: #b3b3b3;">"Class: ~S, Superclasses: ~S~%"</span> |
550 | (class-name class) |
551 | (mapcar #'class-name |
552 | (class-precedence-list class))) |
553 | (<span style="color: #00ffff;">let</span> ((slot-names (mapcar #'slot-definition-name |
554 | (class-slots class)))) |
555 | (format t <span style="color: #b3b3b3;">"Slots: ~%~{ ~S~%~}"</span> slot-names) |
556 | (inspect-loop slot-names instance #'example-inspect)))) |
557 | |
558 | (<span style="color: #00ffff;">defun</span> <span style="color: #87cefa;">inspect-loop</span> (slots instance inspector) |
559 | (format t <span style="color: #b3b3b3;">"Enter slot to inspect or :pop to go up one level: "</span>) |
560 | (finish-output) |
561 | (<span style="color: #00ffff;">let*</span> ((slot (read)) |
562 | (found-slot (member slot slots))) |
563 | (<span style="color: #00ffff;">cond</span> (found-slot |
564 | (funcall inspector (slot-value instance slot)) |
565 | (funcall inspector instance)) |
566 | ((eq slot <span style="color: #b0c4de;">:pop</span>) t) |
567 | (t |
568 | (format t <span style="color: #b3b3b3;">"~S is invalid. Valid slot names: ~S~%"</span> |
569 | slot |
570 | slots) |
571 | (inspect-loop slots instance inspector))))) |
572 | </pre> |
573 | |
574 | |
575 | <h3><a name="sec28" id="sec28"></a> |
576 | Observer Design Pattern</h3> |
577 | |
578 | <p class="first">A simple implementation of the observer pattern is under 100 lines, |
579 | and the user level code requires only a single line of code to make |
580 | any existing class observable.</p> |
581 | |
582 | <p>In a language lacking a MOP, implementing the observer pattern |
583 | requires modifying every accessor of a class to explicitly invoke any |
584 | observers, and neccesitates the addition of a mixin class to the class |
585 | heirarchy. The fact that an object can be observed is a meta property |
586 | of the class, and forcing it to be implemented at the application |
587 | level dirties the inheritance heirarchy and adds uneccesary meta |
588 | details to the program.</p> |
589 | |
590 | <pre class="src"> |
591 | <span style="color: #ff7f24;">;;; </span><span style="color: #ff7f24;">This metaclass adds a slot to instances which use it, and so the |
592 | </span><span style="color: #ff7f24;">;;; </span><span style="color: #ff7f24;">system is defined in its own package to avoid name conflicts |
593 | </span>(<span style="color: #00ffff;">defpackage</span> <span style="color: #98fb98;">:observer</span> |
594 | (<span style="color: #b0c4de;">:use</span> <span style="color: #b0c4de;">:cl</span> #+sbcl <span style="color: #b0c4de;">:sb-mop</span>) |
595 | (<span style="color: #b0c4de;">:export</span> observable register-observer unregister-observer)) |
596 | |
597 | (<span style="color: #00ffff;">in-package</span> <span style="color: #b0c4de;">:observer</span>) |
598 | |
599 | <span style="color: #ff7f24;">;;; </span><span style="color: #ff7f24;">Metaclass |
600 | </span>(<span style="color: #00ffff;">defclass</span> <span style="color: #98fb98;">observable</span> (standard-class) |
601 | () |
602 | (<span style="color: #b0c4de;">:documentation</span> <span style="color: #b3b3b3;">"Metaclass for observable objects"</span>)) |
603 | |
604 | (<span style="color: #00ffff;">defmethod</span> <span style="color: #87cefa;">compute-slots</span> ((class observable)) |
605 | <span style="color: #b3b3b3;">"Add a slot for storing observers to observable instances"</span> |
606 | (cons (make-instance 'standard-effective-slot-definition |
607 | <span style="color: #b0c4de;">:name</span> 'observers |
608 | <span style="color: #b0c4de;">:initform</span> '(make-hash-table) |
609 | <span style="color: #b0c4de;">:initfunction</span> #'(<span style="color: #00ffff;">lambda</span> () (make-hash-table))) |
610 | (call-next-method))) |
611 | |
612 | (<span style="color: #00ffff;">defmethod</span> <span style="color: #87cefa;">validate-superclass</span> ((class observable) |
613 | (super standard-class)) |
614 | t) |
615 | |
616 | (<span style="color: #00ffff;">defun</span> <span style="color: #87cefa;">register-observer</span> (instance slot-name key closure) |
617 | (register-observer-with-class (class-of instance) |
618 | instance |
619 | slot-name |
620 | key |
621 | closure)) |
622 | |
623 | (<span style="color: #00ffff;">defun</span> <span style="color: #87cefa;">unregister-observer</span> (instance slot-name key) |
624 | (unregister-observer-with-class (class-of instance) |
625 | instance |
626 | slot-name |
627 | key)) |
628 | |
629 | (<span style="color: #00ffff;">defun</span> <span style="color: #87cefa;">get-observers</span> (instance slot-name) |
630 | (get-observers-with-class (class-of instance) |
631 | instance |
632 | slot-name)) |
633 | |
634 | (<span style="color: #00ffff;">defun</span> <span style="color: #87cefa;">add-observer-table</span> (instance slot-name) |
635 | (setf (gethash slot-name (slot-value instance |
636 | 'observers)) |
637 | (make-hash-table))) |
638 | |
639 | (<span style="color: #00ffff;">defgeneric</span> <span style="color: #87cefa;">register-observer-with-class</span> (class instance slot-name key closure)) |
640 | (<span style="color: #00ffff;">defgeneric</span> <span style="color: #87cefa;">unregister-observer-with-class</span> (class |
641 | instance |
642 | slot-name |
643 | key)) |
644 | |
645 | (<span style="color: #00ffff;">defmethod</span> <span style="color: #87cefa;">register-observer-with-class</span> ((class observable) |
646 | instance |
647 | slot-name |
648 | key |
649 | closure) |
650 | (setf (gethash key |
651 | (or (gethash slot-name |
652 | (slot-value instance 'observers)) |
653 | <span style="color: #ff7f24;">;; </span><span style="color: #ff7f24;">Lazily add observer hash tables |
654 | </span> (add-observer-table instance slot-name))) |
655 | closure)) |
656 | |
657 | (<span style="color: #00ffff;">defmethod</span> <span style="color: #87cefa;">unregister-observer-with-class</span> ((class observable) |
658 | instance |
659 | slot-name |
660 | key) |
661 | (remhash key (gethash slot-name |
662 | (slot-value instance 'observers)))) |
663 | |
664 | (<span style="color: #00ffff;">defmethod</span> <span style="color: #87cefa;">get-observers-with-class</span> ((class observable) |
665 | instance |
666 | slot-name) |
667 | (gethash slot-name (slot-value instance 'observers))) |
668 | |
669 | (<span style="color: #00ffff;">defmethod</span> (<span style="color: #87cefa;">setf slot-value-using-class)</span> <span style="color: #b0c4de;">:before</span> (new-value |
670 | (class observable) |
671 | instance |
672 | slot) |
673 | (<span style="color: #00ffff;">let</span> ((slot-name (slot-definition-name slot))) |
674 | (<span style="color: #00ffff;">if</span> (not (eq 'observers slot-name)) |
675 | (<span style="color: #00ffff;">let</span> ((observers |
676 | (get-observers instance (slot-definition-name slot)))) |
677 | (<span style="color: #00ffff;">if</span> observers |
678 | (maphash #'(<span style="color: #00ffff;">lambda</span> (key observer) |
679 | (funcall observer |
680 | (<span style="color: #00ffff;">if</span> (slot-boundp instance slot-name) |
681 | (slot-value instance slot-name) |
682 | nil) |
683 | new-value)) |
684 | observers)))))) |
685 | </pre> |
686 | |
687 | |
688 | <h3><a name="sec29" id="sec29"></a> |
689 | Real World</h3> |
690 | |
691 | <h4><a name="sec30" id="sec30"></a> |
692 | <a href="http://common-lisp.net/project/ucw/">UCW</a> and <a href="http://common-lisp.net/project/bese/arnesi.html">Arnesi</a></h4> |
693 | |
694 | <p class="first">Arnesi uses the CLOS MOP to implement methods which are transparantly |
695 | rewritten into continuation passing style. This allows their execution |
696 | to be suspended at certain points and resumed later. UCW builds on top |
697 | of this to support a web framework where the statelessness of http is |
698 | hidden from the user; displaying a page suspends the execution of the |
699 | current continuation, and resumes it upon submission. The user level |
700 | code is completely unaware of this.</p> |
701 | |
702 | |
703 | <h4><a name="sec31" id="sec31"></a> |
704 | <a href="http://clsql.b9.com">CLSQL</a></h4> |
705 | |
706 | <p class="first">CLSQL uses the reflective part of the CLOS MOP to map Common Lisp data |
707 | types into SQL types, and the intercessory protocol for slot |
708 | allocation to map slots onto database columns or sql expressions (for |
709 | implementing relational slots).</p> |
710 | |
711 | |
712 | <h4><a name="sec32" id="sec32"></a> |
713 | <a href="http://common-lisp.net/project/elephant/">Elephant</a></h4> |
714 | |
715 | <p class="first">Elephant uses the CLOS MOP to transparantly store any class to disk |
716 | and handle paging between the disk store and memory efficiently and |
717 | with no user intervention.</p> |
718 | |
719 | |
720 | |
721 | |
722 | <h2><a name="sec33" id="sec33"></a> |
723 | Sources &amp; Further Reading</h2> |
724 | |
725 | <h3><a name="sec34" id="sec34"></a> |
726 | Sources</h3> |
727 | |
728 | <h4><a name="sec35" id="sec35"></a> |
729 | The Art of the Metaobject Protocol</h4> |
730 | |
731 | <h5>Kiczales, Gregor et al. MIT Press 1991</h5> |
732 | |
733 | <p>Highly recommended reading even if you plan to never implement a MOP |
734 | or use the CLOS one. The design principles it recommends are quite |
735 | useful.</p> |
736 | |
737 | |
738 | |
739 | <h4><a name="sec36" id="sec36"></a> |
740 | <a href="http://www.lisp.org/mop/contents.html">CLOS MOP Specification</a></h4> |
741 | |
742 | <p class="first">Specification of the MOP for CLOS defined in <em>The Art of the Metaobject Protocol</em>.</p> |
743 | |
744 | |
745 | <h4><a name="sec37" id="sec37"></a> |
746 | <a href="http://citeseer.ist.psu.edu/399658.html">Metaobject Protocols: Why We Want Them and What Else They Can Do</a></h4> |
747 | |
748 | <p class="first">A short overview of MOP design principles followed by three example |
749 | metaobject protocols for Scheme.</p> |
750 | |
751 | |
752 | <h4><a name="sec38" id="sec38"></a> |
753 | <a href="http://www2.parc.com/csl/groups/sda/projects/oi/towards-talk/transcript.html">Why Are Black Boxes so Hard to Reuse?</a></h4> |
754 | |
755 | <p class="first">Transcription of a talk on the benefits of open implementations of |
756 | software. It first discusses several problems that black box software |
757 | implementations pose, and then presents existing solutions. It shows |
758 | how the existing solutions are insufficient, and then provides |
759 | metaobject protocols as a solution to most of the problems.</p> |
760 | |
761 | |
762 | |
763 | <h3><a name="sec39" id="sec39"></a> |
764 | Further Reading</h3> |
765 | |
766 | <h4><a name="sec40" id="sec40"></a> |
767 | <a href="http://citeseer.ist.psu.edu/chiba95metaobject.html">A Metaobject Protocol for C++</a></h4> |
768 | |
769 | <p class="first">Example of a purely compile time MOP. It implements the functionality |
770 | of a code walker and something similar to the Lisp macro system.</p> |
771 | |
772 | |
773 | <h4><a name="sec41" id="sec41"></a> |
774 | <a href="http://www.parc.com/csl/groups/sda/publications/papers/Kiczales-TUT95/for-web.pdf">Open Implementations and Metaobject Protocols</a></h4> |
775 | |
776 | <p class="first">It is a bit long, but it seems to follow a similar structure to AMOP |
777 | in introducing MOPs and their usefulness. The pages are slides with |
778 | notes, and so the 331 pages might not actually take that long to read.</p> |
779 | |
780 | |
781 | |
782 | |
783 | <!-- Page published by Emacs Muse ends here --> |
784 | |
785 | <p class="cke-buttons"> |
786 | <!-- validating badges, any browser, etc --> |
787 | <a href="http://validator.w3.org/check/referer"><img |
788 | src="http://www.w3.org/Icons/valid-xhtml10" |
789 | alt="Valid XHTML 1.0!" /></a> |
790 | |
791 | <a href="http://www.anybrowser.org/campaign/"><img |
792 | src="img/buttons/w3c_ab.png" alt="[ Viewable With Any Browser |
793 | ]" /></a> |
794 | |
795 | <a href="http://www.debian.org/"><img |
796 | src="img/buttons/debian.png" alt="[ Powered by Debian ]" /></a> |
797 | |
798 | <a href="http://hcoop.net/"> |
799 | <img src="img/buttons/hcoop.png" |
800 | alt="[ Hosted by HCoop]" /> |
801 | </a> |
802 | |
803 | <a href="http://www.fsf.org/register_form?referrer=114"> |
804 | <img src="img/buttons/fsf_member.png" |
805 | alt="[ FSF Associate Member ]" /> |
806 | </a> |
807 | </p> |
808 | |
cb44b69b |
809 | <p class="cke-footer">Mike: I WAS NOT MICROWAVED. |
2aff8b5c |
810 | </p> |
811 | <p class="cke-timestamp">Last Modified: |
812 | March 13, 2008</p> |
813 | </body> |
814 | </html> |